class HTMLElement{
	//ver=10135
/*	
	//HTMLElement __construct([string $html])
	///////////////////////////////////////////////////////
	//void addChild(HTMLElement/string $child[,int $sharp])
	//HTMLElement addSubelement(string $name[,int $sharp])
	//void load(string $html)
	//void exload(string $html,array $arr)
	//void insert(url $file)
	//void rmChild([int $sharp])
	//void setChildren([array/string $children])
	//string getChildren()
	//////////////////////////////////////////////////////////
	//void addAtt(string $key[, string $value])
	//void rmAtt(string $key)
	//void upAtt(string $key, string $value)
	//void setAtts(array/string $atts)
	//string getAtts([$key])
	/////////////////////////////////////////////////////////
	//void addSublink(url $url)
	//void addSpans(array/string $arr)
	//array addArray(CS-string $keys,CS-string $values)
	//HTMLElement ::getSelect(string $name,array $a[,string $v])
	//string ::getOptions(array $arr, string $v[, boolean $haskey])
	//HTMLElement ::getRSelect(string $name,array $a,string $v)
	//string ::getRidos(string $name,array $a,string $v)
	//HTMLElement getJumpMenu(string $m, int $pages, int $page)
*/
	//All empty HTML elements in HTML5.
	const EM='(br|img|hr|embed|input|meta|link|param|wbr)';
	
	//Not-empty HTML elements in HTML 5.
	const NEM='(a|abbr|address|area|article|aside|audio|b|base|bd[io]|blockquote|body|button|canvas|caption|cite|code|col|colgroup|command|datalist|d[dlt]|del|details|dfn|div|em|fieldset|figcaption|figure|footer|form|h[1-6]|head|header|hgroup|html|i|iframe|ins|keygen|kbd|label|legend|li|map|mark|menu|meter|nav|noscript|object|[ou]l|optgroup|option|output|p|pre|progress|q|r[pt]|ruby|s|samp|script|section|select|small|source|span|strong|style|su[bp]|summary|table|tbody|t[drh]|textarea|tfoot|thead|time|title|track|var|video)';
	
	//Old elements that HTML5 dosen't support any more.
	const OLDE='(acronym|applet|basefont|big|center|dir|font|frame|frameset|noframes|strike|tt|u)';
	
	//HTML element regular expression
	const HTMLREG='#
		<(style|script)([^>]*?)>(.*?)</\1>
		|
		<(br|img|hr|embed|input|meta|link|param|wbr)([^>]*?)(?:/?)>
		|
		<(\w{1,10})\b([^>]*?)>((?R)*)</\6>
		|
		([^<>]+)
		#isx';
		
	//HTML element's attributes regular expression
	const ATTSREG=<<<EOF
			{
				\s+(?P<key>\w+)
				(
					=(?P<q>['"])?
						(?P<value>(?(q).*?|[^'"\s]+))
						(?(q)(?P=q))
					|\b(?!['"=])
				)
			}sx
EOF;

	public $name;
	public $atts=array();
	public $children=array();
	
	//Support name/html to inialize. Only legal name can be allowed.
	public function __construct($name='html'){
		$name=self::cleanHTML($name);
		if(!empty($name)){
			$e=self::NEM."|".self::EM;
			$n=preg_match("/^($e)$/i",$name);
			if($n>0){//name
				$this->name=strtolower($name);
			}			
			if($n==0){//html
				$n=preg_match("{<(?P<name>$e)\b(?P<atts>[^>]*?)(?![/])>(?P<children>.*?)</(?P=name)>}i",$name,$arr);
				if($n>0){
					$this->name=strtolower($arr['name']);
					$this->setAtts($arr['atts']);
					$this->load($arr['children']);
				}
			}
		}
	}
	
	//Output the string of HTML.
	public function __toString(){
		$name=$this->name;
		$str="<$name";
		if(!empty($this->atts)){
			foreach($this->atts as $key=>$val)
				$str.=" $key=\"$val\"";
		}
		$str.=">";
		$isfull=true;
		if(preg_match("/^".self::EM."$/i",$name)>0)
			$isfull=false;
		if(preg_match("/^".self::NEM."$/i",$name)>0)
			$isfull=true;
		if($isfull){
			if(!empty($this->children)){
				foreach($this->children as $child)
					$str.=(string)$child;
			}
			$str.="</$name>";
		}
		return $str;
	}
	
	//To get rid of whitespace and other unprintable character from the string $s.
	static function cleanHTML($s){
		$s=trim("$s");
		$s=preg_replace('/\t|\f|\r|\n/','',$s);//remove tab,carriage,newline
		$s=preg_replace('/<!--.*?-->/','',$s);//remove comments
		$s=preg_replace('/\x20+</','<',$s);
		$s=preg_replace('/>\x20+/','>',$s);
		return $s;
	}
////////////////////////////////////////////////////
/////////Functions for Children.////////////////////
////////////////////////////////////////////////////

	//Add an object or a string as a child.
	//$sharp begins with 0, and -1 means the last child.
	public function addChild($child,$sharp=-1){
		if(!($child instanceof HTMLElement))
			$child=trim($child);
		$n=count($this->children);
		if(!is_int($sharp)||$sharp<0)
			$sharp=$n;
		if($child!='')//'0' is empty.
			array_splice($this->children,$sharp,0,array($child));
			//$child may be an object.
	}
	
	//Add a child by name, and return the child as an object.
	public function addSubelement($name,$sharp=-1){
		$ele=new HTMLElement($name);
		$this->addChild($ele,$sharp);
		return $ele;
	}
	
	//Add children in batch by html code.
	public function load($html){
		$html=self::cleanHTML($html);
		if($html=='')return false;
		$n=preg_match_all(self::HTMLREG,$html,$arr,PREG_SET_ORDER);		
		if(!empty($arr)){
			foreach($arr as $ele){
				//style or script
				if(isset($ele[1])&&!empty($ele[1])){
					$obj=new HTMLElement($ele[1]);
					$obj->setAtts($ele[2]);
					$obj->addChild($ele[3]);
					$this->addChild($obj);
				}
				//empty elements.
				if(isset($ele[4])&&!empty($ele[4])){
					$obj=new HTMLElement($ele[4]);
					$obj->setAtts($ele[5]);
					$this->addChild($obj);
				}
				//normal elements.
				if(isset($ele[6])&&!empty($ele[6])){
					$obj=new HTMLElement($ele[6]);
					$obj->setAtts($ele[7]);
					$obj->load($ele[8]);
					$this->addChild($obj);
				}
				//texts.
				if(isset($ele[9])&&!empty($ele[9])){
					$this->addChild($ele[9]);
				}				
			}
		}
	}
	
	//Extended function load(), surporting %VAR% varibles.
	public function exload($html,$arr){
		$str=self::cleanHTML($html);
		if(is_array($arr)){
			foreach($arr as $var)
				$str=preg_replace('/%VAR%/',$var,$str,1);
		}
		else
			$str=preg_replace('/%VAR%/',$arr,$str,1);
		$this->load($str);	
	}
	
	//Insert an external pain file into the current object, as part of the value.
	public function insert($file){
		if(is_file($file)){
			$string=file_get_contents($file);
			$this->addChild(self::cleanHTML($string));
		}else
			return false;
	}
	
	//Remove a child according to $sharp, the last child by default.
	public function rmChild($sharp=-1){
		$n=count($this->children);
		if(!is_int($sharp)||$sharp<0)
			$sharp=$n-1;
		if($n>0){			
			unset($this->children[$sharp]);
			$this->children=array_values($this->children);
		}
	}
	
	//Set all children rapidly to a children array, clear up all children by default.
	public function setChildren($children=array()){
		$this->children=array();
		if(!empty($children)){
			if(is_array($children))
				$this->children=$children;
			if(is_string($children))
				$this->children[]=$children;
		}
	}
	
	//Return the string of children.
	public function getChildren(){
		$str="";
		if(!empty($this->children)){
			foreach($this->children as $child){
				$str.=(string)$child;
			}
		}
		return $str;
	}
	
	
//////////////////////////////////////////////////////	
/////////   Functions for Attributes    //////////////
//////////////////////////////////////////////////////

	//Add a new attributte, with key=>value pair.
	//If the key exists, the value will be ignored.
	public function addAtt($key,$value=''){
		$key=trim($key);
		$value=trim($value);
		if(!empty($key)&&!array_key_exists($key,$this->atts)){
				$this->atts[$key]=$value;
		}
	}
	
	//Remove an attributte by key.
	public function rmAtt($key){
		$key=trim($key);
		if(array_key_exists($key,$this->atts))
			unset($this->atts[$key]);
	}
	
	//Update an attributte's value.
	//If the key doesn't exist, a new one will be added.
	public function upAtt($key,$value){
		$key=trim($key);
		$value=trim($value);
		if(!empty($key)){
			if(array_key_exists($key,$this->atts))
				$this->atts[$key]=$value;
			else
				$this->addAtt($key,$value);
		}
	}
	
	//Set all attributes to an array.
	//$atts also supports a string argument.
	public function setAtts($atts=array()){
		$this->atts=array();
		if(!empty($atts)){
			if(is_array($atts))
				$this->atts=$atts;
			if(is_string($atts)){
				$arr=array();
				preg_match_all(self::ATTSREG,$atts,$arr_atts,PREG_SET_ORDER);
				foreach($arr_atts as $it){
					$key=strtolower($it['key']);
					$arr[$key]=$it['value'];
				}					
				$this->setAtts($arr);
			}
		}
	}
	
	//Return the value of an attribute by key.
	//If $key is empty, return all attributes string.
	public function getAtts($key=''){
		$key=trim($key);
		if(!empty($key)&&array_key_exists($key,$this->atts)){
			return $this->atts[$key];
		}
		if(!empty($this->atts)){
			$str="";
			foreach($this->atts as $key=>$value)
				$str.=" $key=\"$value\"";
			return $str;
		}
	}	
	
//////////////////////////////////////////////////////////
////////   Extended Functions for HTML Element   /////////
//////////////////////////////////////////////////////////

	//Add superlink to the children.
	public function addSublink($url){
		if(!empty($url)){
			$a=new HTMLElement('a');
			$a->addAtt('href',$url);
			$a->setChildren($this->children);
			$this->addChild($a);
		}
	}
	
	//Add one or more spans to the sub.
	public function addSpans($arr){
		if(is_array($arr)){
			foreach($arr as $var)
				$this->addSpans($var);
		}
		else{
			$arr=trim("$arr");
			if($arr!=''){
				$span=new HTMLElement('span');
				$span->addChild($arr);
				$this->addChild($span);
			}
		}		
	}
	
	//Combine two arrays to an associated array.
	static function getArray($keys,$values){
		$arr_k=explode(',',$keys);
		$arr_v=explode(',',$values);
		if(count($arr_k)==count($arr_v))
			return array_combine($arr_k,$arr_v);
	}
	
	//Return a select object of HTMLElement class.
	static function getSelect($name,$a,$v=''){
		$sel=new HTMLElement('select');
		$sel->addAtt('name',$name);
		$arr=(key($a)===0)?array_combine($a,$a):$a;
		foreach($arr as $key=>$val){
			$opt=new HTMLElement('option');
			$opt->addAtt('value',$val);
			$opt->addChild($key);
			if($key==$v)
				$opt->addAtt('selected','selected');
			$sel->addChild($opt);
		}
		return $sel;
	}
	
	//Return a string of options.
	static function getOptions($arr,$v=0,$haskey=false){
		$str="";
		if($haskey){
			foreach($arr as $key=>$val){
				$opt=new HTMLElement('option');
				$opt->addAtt('value',$key);
				$opt->addChild($val);
				if($key==$v)
					$opt->addAtt('selected','selected');
				$str.=(string)$opt;
			}
		}
		else{
			$i=0;
			foreach($arr as $vr){
				$opt=new HTMLElement('option');
				$opt->addAtt('value',$i);
				$opt->addChild($vr);
				if($i==$v)
					$opt->addAtt('selected','selected');
				$str.=(string)$opt;
				$i++;
			}
		}
		return $str;
	}
	
	//Return a select object of HTMLElement class, which $key is reversed.
	static function getRSelect($name,$a,$v){
		$sel=new HTMLElement('select');
		$sel->addAtt('name',$name);
		foreach($a as $key=>$val){
			$opt=new HTMLElement('option');
			$opt->addAtt('value',$key);
			$opt->addChild($val);
			if($key==$v)
				$opt->addAtt('selected','selected');
			$sel->addChild($opt);
		}
		return $sel;
	}

	//Return a string of radios.
	static function getRadios($name,$a,$v){
		$str='';
		$arr=(key($a)===0)?array_combine($a,$a):$a;
		foreach($arr as $key=>$val){
			$rad=new HTMLElement('input');
			$rad->addAtt('type','radio');
			$rad->addAtt('name',$name);
			$rad->addAtt('value',$val);
			if($key==$v)
				$rad->addAtt('checked','checked');
			$span=new HTMLElement('span');
			$span->addChild($key);
			$lab=new HTMLElement('label');
			$lab->addChild($rad);
			$lab->addChild($span);
			$str.=(string)$lab;
		}
		return $str;
	}
	
	//Return a jump menu of select object.
	static function getJumpMenu($m,$pages,$page){
		$menu=self::getSelect('',range(1,$pages),$page);
		$menu->addAtt('id','menu');
		$menu->addAtt('onchange',"window.location='?v=$m&amp;page='+this.value");
		return $menu;
	}
}
